/*
 * Copyright 2008-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.appengine.twitter.guice;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.servlet.http.HttpServlet;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.orm.jpa.JpaTemplate;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import com.google.appengine.twitter.domain.model.UserRepository;
import com.google.appengine.twitter.exception.ServletExceptionHandler;
import com.google.appengine.twitter.exception.ServletMethodInterceptor;
import com.google.appengine.twitter.exception.SimpleServletExceptionHandler;
import com.google.appengine.twitter.infrastructure.persistent.jpa.JpaUserRepository;
import com.google.appengine.twitter.interfaces.web.CreateAccountServlet;
import com.google.appengine.twitter.interfaces.web.HomeServlet;
import com.google.appengine.twitter.interfaces.web.ListAccountServlet;
import com.google.appengine.twitter.interfaces.web.SigninServlet;
import com.google.appengine.twitter.interfaces.web.SignupServlet;
import com.google.appengine.twitter.security.AuthenticationManager;
import com.google.appengine.twitter.security.PasswordEncoder;
import com.google.appengine.twitter.security.PlainTextPasswordEncoder;
import com.google.appengine.twitter.security.SimpleAuthenticationManager;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Module;
import com.google.inject.Provider;
import com.google.inject.Scopes;
import com.google.inject.matcher.Matchers;
import com.google.inject.servlet.ServletModule;

/**
 *
 * @author politics wang
 * @since Apr 23, 2009
 *
 */
public abstract class TwitterInjectors {

	public static Injector create() {
		return Guice.createInjector(new ServletModule() {
			@Override
			protected void configureServlets() {
				install(createNoneWebModule());
				
//				filter("/*", null).through(arg0)
				
				configureServeWith();
				bindServletInterceptors();
			}

			private void configureServeWith() {
				serve("/signup").with(SignupServlet.class);
				serve("/account/create").with(CreateAccountServlet.class);
				serve("/admin/account/list").with(ListAccountServlet.class);
				serve("/sessions").with(SigninServlet.class);
				serve("/home").with(HomeServlet.class);
			}
			
			private void bindServletInterceptors() {
				MethodInterceptor[] servletInterceptors = createServletInterceptors();
				bindInterceptor(Matchers.subclassesOf(HttpServlet.class), Matchers.any(), servletInterceptors);
				for (MethodInterceptor interceptor : servletInterceptors) {
					requestInjection(interceptor);
				}
			}

		});
	}
	
	private static MethodInterceptor[] createServletInterceptors() {
		List<MethodInterceptor> servletInterceptors = new ArrayList<MethodInterceptor>();
		servletInterceptors.add(new ServletMethodInterceptor());
		return servletInterceptors.toArray(new MethodInterceptor[0]);
	}
	
	public static Module createNoneWebModule() {
		return new AbstractModule() {
			@Override
			protected void configure() {
				EntityManagerFactory emf = Persistence.createEntityManagerFactory("transactions-optional");
				bindBeans(emf);
				bindInterceptors(emf);
			}

			private void bindBeans(final EntityManagerFactory emf) {
				bind(EntityManagerFactory.class).toProvider(new Provider<EntityManagerFactory>() {
					@Override
					public EntityManagerFactory get() {
						return emf;
					}
				}).in(Scopes.SINGLETON);
				
				bind(JpaTemplate.class).toInstance(new JpaTemplate(emf));
				
				bind(ServletExceptionHandler.class).to(SimpleServletExceptionHandler.class).in(Scopes.SINGLETON);
				bind(PasswordEncoder.class).to(PlainTextPasswordEncoder.class).in(Scopes.SINGLETON);
				bind(UserRepository.class).to(JpaUserRepository.class).in(Scopes.SINGLETON);
				bind(AuthenticationManager.class).to(SimpleAuthenticationManager.class).in(Scopes.SINGLETON);
			}

			private void bindInterceptors(EntityManagerFactory emf) {
				PlatformTransactionManager ptm = new JpaTransactionManager(emf);
				Properties tas = new Properties();
				tas.setProperty("*", "PROPAGATION_REQUIRED");
				TransactionInterceptor transactionInterceptor = new TransactionInterceptor(ptm, tas);
				transactionInterceptor.afterPropertiesSet();
				bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transactional.class), transactionInterceptor);
				requestInjection(transactionInterceptor);
			}
		};
	}
	
}
